/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014年7月4日
 * V4.0
 */
package com.jphenix.driver.threadpool;

import com.jphenix.driver.cluster.FCluster;
import com.jphenix.driver.serialno.FSN;
import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.kernel.script.ScriptUtil;
import com.jphenix.kernel.script.ScriptVO;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SInteger;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.DebugUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.beans.IBase;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.script.IScriptBean;
import com.jphenix.standard.serialno.ISN;
import com.jphenix.standard.servlet.ICluster;
import com.jphenix.standard.threadpool.ICrontabBean;

import java.util.ArrayList;
import java.util.List;

/**
 * 定时任务信息VO 
 * 
 * 最新定时格式：
 * 
 * 特点：兼容Linux的Crontab格式，也采纳了Spring定时格式的优点，去掉其缺点。
 * Spring定时格式的缺点：周定义， 1为周日（欧洲标准）。而我们认为1为周一，7为周日（中国，美国，ISO标准）
 *                       复杂，定时时间容易让人产生误解。比如：日期设置为5W，即当月离第五个日期最近的工作
 *                       有可能小于当月第五个日期，有可能大于第五个日期。还有很少用到的关键字C。在设计定时
 *                       字符串时，不知道该用到*还是?（有时用*和用?都行）
 *                       
 * 于是我们本着不能将就用的原则，自己写了个定时任务。
 * 
 * 规则如下：
 * 
 * 
 * 如果分为5段信息
 * * * * * *
 * 则每段含义为： 分 时 日 月 周
 * 
 * 如果分为6段信息
 * 则每段含义为： 秒 分 时 日 月 周
 * 
 * 如果分为7段信息
 * 则每段含义为： 秒 分 时 日 月 周 年
 * 如果需要用到年，必须有秒信息，如果没有用到秒，则用 * 占位
 * 年很少用得到，但开发成本低，一顺手就加上了
 * 
 * 每个分段描述说明：
 * 
 *    *  星号：代表未设置。比如这种情况 * 10 * * *  每天10点执行，但是没设置分值（严格讲，这是错误的），分钟
 *             默认为0
 *             
 *    数值   ：
 *             秒、分钟、小时：0~59   
 *             天            ：1~31（视具体月份而定） 
 *             月            ：1~12 
 *             周            ：0~7（注意：0、7都是周日，用哪个都行） 注意：ISO标准定义7为周日（一个周期最后一天），欧洲周日为1（不采纳）
 *             年            ：当前年份~无限大
 *             
 *    枚举   ：1,3,5,6  适用于 秒、分钟、小时、天、月、周。
 *    
 *    范围   ：1-5      适用于 秒、分钟、小时、天、月、周。
 * 
 *    单词缩写
 *    
 *          月份： Jan   Feb   Mar   Apr   May    Jun    Jul   Aug    Sept   Oct   Nov   Dec （不计大小写）
 *          
 *            周： Mon  Tue   Wed   Thur   Fri   Sat   Sun
 *            
 *    间隔循环： * /1  （注意：星号跟斜杠之间没有空格。这里只能带上空格，否则被认定为注释结束）
 *                      适用于 秒、分钟、小时、天、月、周、年
 *                      比如：用在分钟上，代表每一分钟执行一次 * /5 为每5分钟执行一次
 *                
 *               5 /1   组合信息，如果用在分钟上，代表从第5分钟开始，每分钟执行一次
 *              
 *               5-10 /1 如果用在日期上，意为：从5号到10号，每天执行一次
 *               
 *    末尾关键字L   适用于：天，星期。比如 30 10 5L * *  意为：每月倒数第五天得的10点半执行。
 *                  因为每个月的日期数量是不一样的，用这个关键字就方便多了。比如：30 10 * * 5L
 *                  意为：每个月最后一周的周五的10点半执行。
 *            
 *    末尾关键字W   代表工作日，仅适用于天。比如 * /1W 每个工作日执行一次
 *                                               10W   每月第十个工作日
 *                                               1WL   每个月最后一个工作日
 *                   注意：如果使用到了工作日，需要设置工作日序列或非工作日序列，否则程序无法判断指定日期是否为工作日
 *                   
 *                        如果这样设置  1,2,3  则包含了 3.   如果这样设置  1-3 实际上到2就结束了，不包含3，比如：
 *                        30 8-17 * * * 每天从早上8点开始，到晚上17点结束，每30分钟执行一次。  实际上到了16:59分以后，就不在执行了
 *                        也就是说，17点以后（包含17点）不再执行 （1-5 不包含末尾数值，这种方式仅适用于 小时、分、秒。 日、月、周、年，是包含末尾数值的）
 *                        
 *                        30 * * * * 与  * /30 * * * * 的区别（斜杠与前面的星号之间没有空格，这里带空格是因为避免被误认为注释结束）
 *                        前一种情况，30分整执行。 后一种情况，比如第一次执行时间为 50分，那么下一次执行时间为下一个小时的20分。
 *                   
 *  常用例子：
 *  
 *      30 08 * * *                每天的8点半执行
 *      30 08 * * 1-5              周一到周五，每天8点半执行
 *      30 08 * Jan,Feb,Mar        每一月份、二月份、三月份的每天8点半执行一次
 *      30 08 * * Tue              每周二的8点半执行一次
 *      * /5 08-10 * * *           每天8点到10点之间，每5分钟执行一次
 *      * /3 * * * * *             每三秒执行一次
 *      * /3 * 10 * * *            每天10点，每三秒执行一次
 *      * 00 09 1 1 * * /2         每两年的一月一号9点执行一次
 *      30 08 * * /1 5L            每个月最后一个周五的早上8点半执行
 *      0/5 20-23,0-8 * * *        每晚8点到第二天早上8点期间，每5分钟执行一次
 *      * /3 * * * * *             每3秒执行一次（从启动时当前秒值开始）
 *      0/3 * * * * *              没3秒执行一次，从0秒开始
 *      3 * * * * *                从每分钟的第3秒开始执行
 *      
 *      
 *  计算下一个到期时间的方法，采用的是进化开发模式。
 *  所谓进化开发模式：相比传统开发模式，先思考传入值的规律或者程序处理规律，并按照规律开发出
 *                  可以正确处理各种情况的传入值。 而进化开发模式，先考虑每种情况的传入值，
 *                  针对每种情况的传入值进行处理。
 *                  
 *  进化开发模式优点： 没有很深奥的算法，可以直接着手开发。
 *  
 *            缺点： 代码量大，前期程序错误很多，需要不断的通过发现的问题来完善程序（进化方式）
 *                  有时候解决一个问题，以前传入并执行没问题的参数，会再次出现问题。
 *                                                                                                                                                                    <br />
 *  
 *  支持数据库事务处理
 *  
 *  2018-05-24 重新设计了检测是否需要触发执行机制。还增加了获取下一触发时间方法
 *  2018-06-01 修改了设置每月指定时间执行时的错误。设置了指定小时却没指定分钟时，默认设置为0分钟（整点执行）
 *  2018-06-16 全部开发完毕
 *  2018-06-19 修改了指定周信息时，计算时间错误
 *             1-5 不包含末尾数值，这种方式仅适用于 小时、分、秒。 日、月、周、年，是包含末尾数值的
 *             
 *  2018-06-29 修改了解析周信息时，忽略了当天符合条件未执行问题
 *  2018-07-02 启动线程方式执行任务
 *  2018-07-04 修改了设置星期范围后，计算定时时间错误
 *  2018-07-06 解决了第二次响应到期时间时，执行了两次任务的问题。
 *  2019-04-09 为脚本信息类增加了统计信息，以及报错信息统计
 *  2019-06-26 修改了序列号生成器构造方法
 *  2020-01-07 增加了定时任务是否禁用的开关
 *  2020-01-22 完善了注释，写了秒级定时任务时间设定例子
 *  2020-08-26 调用定时任务拦截异常从Exception改为Throwable
 *                                                                                                                    
 * @author 马宝刚
 * 2014年7月4日
 */
@ClassInfo({"2020-08-26 22:59","定时任务信息VO"})
public class CrontabVO extends ABase {
    
	private SDate nextDate                = null;   //下一个执行时间对象
	private SDate lastDate                = null;   //上一个执行时间对象
	private SDate testDate                = null;   //测试时的下一个时间
	                                                
    private long nextDateLong             = 0;      //下一个执行时间（毫秒）
    private long lastDateLong             = 0;      //上一个执行时间（毫秒） 
                                                    
    private String name                   = null;   //任务名
    private String timeInfo               = null;   //时间信息
    private String dbTransactionkey       = null;   //数据库事务处理主键
                                                    
    private ICrontabBean cb               = null;   //定时任务执行类
                                                    
    private ArrayList<Integer> yearList   = null;   //年信息序列
    private ArrayList<Integer> weekList   = null;   //周信息序列
    private ArrayList<Integer> monthList  = null;   //月信息序列
    private ArrayList<Integer> dayList    = null;   //日信息序列
    private ArrayList<Integer> hourList   = null;   //小时信息序列
    private ArrayList<Integer> minuteList = null;   //分钟信息序列
    private ArrayList<Integer> secList    = null;   //秒信息序列
                                                    
    private List<String> workDayList      = null;   //工作日日期序列   日期格式 yyyy-mm-dd
    private List<String> unWorkDayList    = null;   //非工作日日期序列 日期格式 yyyy-mm-dd
                                          
    private int intervalSec               = 0;      //间隔秒
    private int intervalMinute            = 0;      //间隔分钟
    private int intervalHour              = 0;      //间隔小时
    private int intervalDay               = 0;      //间隔天
    private int intervalMonth             = 0;      //间隔月
    private int intervalWeek              = 0;      //间隔多少周
    private int intervalYear              = 0;      //间隔年
    
    //只有小时、分、秒  1-5 不包含5的。 日、月、周、年 1-5 是包含5的，按照国人常理（日是天的意思，不是骂人话）
    private Integer notIncLastMinute      = null;   //如果设置了分范围，并且该值不为空，则范围中除去该值
    private Integer notIncLastHour        = null;   //如果设置了分范围，并且该值不为空，则范围中除去该值
    
    private boolean disabled              = false;  //是否禁止执行（返回最大年限值，避免执行）
                                          
    private List<String> weekNameList     = null;   //周名称序列
    private List<String> monthNameList    = null;   //月份名称序列
    
    public  boolean  noOutLog             = false;  //是否不输出日志
    private boolean  isRunning            = false;  //是否正在运行中
    private ICluster cluster              = null;   //集群管理类
    private boolean  clusterCtrl          = false;  //是否受集群控制
    private boolean  workDayMode          = false;  //是否为工作日模式
    private boolean  lastDayMode          = false;  //是否为当月倒数第几天模式
    private boolean  lastWeekMode         = false;  //是否为当月倒数周几模式
    private boolean  noSecInfo            = true;   //是否不存在秒信息
    private ISN      sn                   = null;   //序列号生成器
    
    /**
     * 构造函数
     * @author 马宝刚
     */
    public CrontabVO(IBase base) {
        super();
        setBase(base);
        //构造集群管理类
        cluster = FCluster.cluster(this);
    }
    
    
    /**
     * 注意：仅用在调试当前解析类
     * 构造函数
     * @author MBG
     */
    public CrontabVO(String timeInfo) {
    	super();
    	this.timeInfo = timeInfo;
    	testDate = new SDate();
    	parse();
    }
    
    
    /**
     * 执行任务方法（线程执行模式）
     * 2018年7月2日
     * @author MBG
     */
    public void invokeTask(){
    	if(clusterCtrl && !cluster.master() || cb==null || isRunning) {
    		//启用了集群控制，但是当前服务器不是主服务器
    		return;
    	}
    	if(!cb.enabled()) {
    		log("!!!!! The Crontab Task Name:["+getName()+"] Has Disabled Ignored !!!!!");
    		return;
    	}
    	isRunning = true;
    	//当前是独立线程，最开始运行部分，在这个部分控制是否输出日志
    	outLog(!noOutLog);
    	
    	log("Begin Invoke The Crontab Task Name:["+getName()+"] TimeInfo:["+getTimeInfo()+"]");
    	
        //建立事务主键
        if(dbTransactionkey!=null && dbTransactionkey.length()>0) {
            ScriptUtil.createDbTransaction(dbTransactionkey,this);
        }
        long _scriptBeforeExecuteTime = System.currentTimeMillis(); //look the var name boy
        try {
            cb.run(); //执行定时任务
            //执行提交事务
            if(dbTransactionkey!=null && dbTransactionkey.length()>0) {
                ScriptUtil.commitDbTransaction(dbTransactionkey,this);
            }
        }catch(Throwable e) {
            e.printStackTrace();
            error("Crontab Invoke Task Exception BeanID:["+name+"] TimeInfo:["+timeInfo+"]",e);
            
            //执行回滚事务
            if(dbTransactionkey!=null && dbTransactionkey.length()>0) {
                ScriptUtil.rollbackDbTransaction(dbTransactionkey,this);
            }
            if(cb instanceof IScriptBean) {
            	((IScriptBean)cb).getScriptLoader().setScriptRunError(((IScriptBean)cb).getScriptId(),ts(),DebugUtil.getExceptionInfo(e));
            }
        }finally {
        	outLog(true);
        	isRunning = false;
            //执行关闭事务
            if(dbTransactionkey!=null && dbTransactionkey.length()>0) {
                ScriptUtil.closeDbTransaction(dbTransactionkey,this);
            }
            if(cb instanceof IScriptBean) {
            	//获取该脚本对应的信息类实例
            	ScriptVO sVO = ((IScriptBean)cb).getScriptVO();
            	sVO.lastExecuteTime=System.currentTimeMillis();
            	sVO.taskRunCount++;
                _scriptBeforeExecuteTime = System.currentTimeMillis()-_scriptBeforeExecuteTime;
                if(sVO.maxExecuteTime<_scriptBeforeExecuteTime){sVO.maxExecuteTime=_scriptBeforeExecuteTime;}
                if(_scriptBeforeExecuteTime!=0 && (sVO.minExecuteTime==0 || sVO.minExecuteTime>_scriptBeforeExecuteTime)){
                	sVO.minExecuteTime=_scriptBeforeExecuteTime;
                }
            }
        }
    }
    
    
    /**
     * 获取任务名
     * @return 任务名
     * 2014年7月5日
     * @author 马宝刚
     */
    public String getName() {
        if(name==null) {
            return "";
        }
        return name;
    }
    
    /**
     * 获取月份名称对应的序号序列
     * @return  序号序列
     * 2014年7月4日
     * @author 马宝刚
     */
    protected List<String> getMonthNameList(){
        if(monthNameList==null) {
            monthNameList = new ArrayList<String>();
            monthNameList.add("_no_month_info_"); //索引0 占位符。
            monthNameList.add("jan");
            monthNameList.add("feb");
            monthNameList.add("mar");
            monthNameList.add("apr");
            monthNameList.add("may");
            monthNameList.add("jun");
            monthNameList.add("jul");
            monthNameList.add("aug");
            monthNameList.add("sept");
            monthNameList.add("oct");
            monthNameList.add("nov");
            monthNameList.add("dec");
        }
        return monthNameList;
    }
    
    /**
     * 获取周名称对应的序号
     * @return 周名称对应的序号序列
     * 2014年7月4日
     * @author 马宝刚
     */
    protected List<String> getWeekNameList(){
        if(weekNameList==null) {
            weekNameList = new ArrayList<String>();
            weekNameList.add("_no_week_info_"); //索引0 占位符，周一为1 周日为7 0也可以为周日，但是在解析值时会将0转为7
            weekNameList.add("mon");
            weekNameList.add("tue");
            weekNameList.add("wed");
            weekNameList.add("thur");
            weekNameList.add("fri");
            weekNameList.add("sat");
            weekNameList.add("sun");
        }
        return weekNameList;
    }
    
    /**
     * 获取上一次执行的时间，没执行返回空字符串
     * @return 上一次执行的时间，没执行返回空字符串
     * 2014年7月5日
     * @author 马宝刚
     */
    public String getLastExecuteDate() {
        if(lastDate==null) {
            return "";
        }
        return lastDate.getDateTime();
    }
    
    /**
     * 获取下次执行时间
     * @return 下次执行时间
     * 2018年7月4日
     * @author MBG
     */
    public String getNextExecuteDate() {
    	if(nextDate==null) {
    		return "";
    	}
    	return nextDate.getDateTime();
    }
    
    
    /**
     * 当前任务是否运行中
     * @return 当前任务是否运行中
     * 2016年5月26日
     * @author MBG
     */
    public boolean isRunning() {
    	return isRunning;
    }

    
    /**
     * 设置定时任务信息
     * @param name 任务名
     * @param timeInfo 定时任务信息
     * @param cmd       执行命令
     * @param dbTransactionkey 数据库事务处理主键
     * 2014年7月4日
     * @author 马宝刚
     */
    public boolean setInfo(String name,String timeInfo,String dbTransactionkey,String cmd) {
        this.name = name;
        this.timeInfo = timeInfo;
        this.dbTransactionkey = dbTransactionkey;
        return parse();
    }
    
    /**
     * 设置定时任务信息
     * @param name 任务名
     * @param timeInfo 定时任务信息
     * @param dbTransactionkey       数据库事务处理主键
     * @param cb 需要定时执行的类
     * 2014年7月4日
     * @author 马宝刚
     */
    public boolean setInfo(String name,String timeInfo,ICrontabBean cb) {
        this.name = name;
        this.timeInfo = timeInfo;
        this.cb = cb;
        if(cb instanceof IScriptBean) {
        	//获取脚本配置信息
        	ScriptVO sVO = ScriptVO.getScriptInfo(cb.getClass());
        	clusterCtrl = sVO.clusterMasterTaskRun; //是否启用集群控制，并且只有主服务器才运行定时任务
        	noOutLog = sVO.noOutLog; //是否配置了禁止输出日志
        	if(sVO.isDbTransaction) {
        		dbTransactionkey = getSID();
        	}
        }
        return parse();
    }
    
    /**
     * 设置定时任务信息
     * @param name 任务名
     * @param timeInfo 定时任务信息
     * @param dbTransactionkey       数据库事务处理主键
     * @param cb 需要定时执行的类
     * 2014年7月4日
     * @author 马宝刚
     */
    public boolean setInfo(String name,String timeInfo) {
        this.name = name;
        this.timeInfo = timeInfo;
        return parse();
    }
    
    /**
     * 返回是否需要数据库事务处理
     * @return 是否需要数据库事务处理
     * 2015年11月23日
     * @author 马宝刚
     */
    public String getDbTransaction() {
        return dbTransactionkey;
    }
    
    /**
     * 获取间隔时间信息
     * @return 间隔时间信息
     * 2014年10月20日
     * @author 马宝刚
     */
    public String getTimeInfo() {
        return timeInfo;
    }
    
    /**
     * 获取下一个执行时间
     * @param curDate 当前时间字符串
     * @return 下一个执行事件字符串
     * 2018年5月23日
     * @author MBG
     */
    public String nextOnTime(String curDate) {
    	return nextOnTime(new SDate(curDate));
    }
    
    /**
     * 获取下一个执行时间
     * @param curDate 当前时间对象
     * @return 下一个执行时间字符串
     * 2018年5月23日
     * @author MBG
     */
    public String nextOnTime(SDate curDate) {
    	return sNextOnTime(curDate).getDateTime();
    }
    
    /**
     * 获取下一个执行时间
     * @return 下一个执行时间字符串
     * 2018年5月23日
     * @author MBG
     */
    public SDate sNextOnTime() {
    	return sNextOnTime(lastDate);
    }
    
    /**
     * 解析时间信息和执行类信息
     * @return 解析成功还是失败
     * 2014年7月4日
     * @author 马宝刚
     */
    private boolean parse() {
        int point = 0;    //分割点
        disabled  = true; //是否禁止执行（在解析前标记为真）
        if(timeInfo==null || timeInfo.length()<1) {
            return false;
        }
        timeInfo = fixTimeInfo(timeInfo); //去掉多余的空格
        //解析时间信息段
        String[] infos = BaseUtil.split(timeInfo.toLowerCase()," ");
        //信息元素序列
        List<String> infoStrList = new ArrayList<String>();
        for(String ele:infos) {
            if(ele==null || ele.length()<1) {
                continue;
            }
            infoStrList.add(ele);
        }
        
        //如果有5个元素，则是 分，时，日，月，周
        //如果有6个元素，则是 秒，分，时，日，月，周
        //如果有7个元素，则是 秒，分，时，日，月，周，年
        
        if(infoStrList.size()<5) {
        	warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:000",null);
            return false;
        }
        
        yearList     = new ArrayList<Integer>();        //年信息序列
        weekList     = new ArrayList<Integer>();        //周信息序列
        monthList    = new ArrayList<Integer>();        //月信息序列
        dayList      = new ArrayList<Integer>();        //日信息序列
        hourList     = new ArrayList<Integer>();        //小时信息序列
        minuteList   = new ArrayList<Integer>();        //分钟信息序列
        secList      = new ArrayList<Integer>();        //秒信息序列
        
        int sInfo    = 0; //起始点
        int eInfo    = 0; //结束点
        String info  = null; //信息段
        
        List<String> infoList = null;
        //秒   分   时   日   月   周   年
        
        int infoStrPoint = 0; //信息元素指针
        String infoEle; //信息元素
        
        /*
         * 处理秒信息
         */
        if(infoStrList.size()>5) {
        	noSecInfo = false;
            infoEle   = infoStrList.get(infoStrPoint++);
            point     = infoEle.indexOf("/");
            if(point>0) {
            	disabled    = false;
                intervalSec = sint(infoEle.substring(point+1));
                infoEle     = infoEle.substring(0,point);
            }
            if(!"*".equals(infoEle)) {
            	disabled = false;
                point    = infoEle.indexOf(",");
                if(point>0) {
                    infoList = BaseUtil.splitToList(infoEle,",");
                }else {
                    infoList = new ArrayList<String>();
                    infoList.add(infoEle);
                }
                for(String ele:infoList) {
                    point = ele.indexOf("-");
                    if(point>0) {
                        sInfo = sint(ele.substring(0,point));
                        eInfo = sint(ele.substring(point+1));
                        if(sInfo<0 || sInfo>59 || eInfo<0 || eInfo>59 || sInfo>eInfo) {
                            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:001",null);
                            return false;
                        }
                        for(int i=sInfo;i<=eInfo;i++) {
                            if(!secList.contains(i)) {
                            	secList.add(i);
                            }
                        }
                    }else {
                        sInfo = sint(ele);
                        if(sInfo>-1 && sInfo<60) {
                        	secList.add(sInfo);
                        }
                    }
                }
            }
        }
        
        
        /*
         * 处理分钟
         */
        if(!(infoStrList.size()>infoStrPoint)) {
            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:111",null);
        }
        infoEle = infoStrList.get(infoStrPoint++);
        point = infoEle.indexOf("/");
        if(point>0) {
        	disabled       = false;
            intervalMinute = sint(infoEle.substring(point+1));
            infoEle        = infoEle.substring(0,point);
        }
        if(!"*".equals(infoEle)) {
        	disabled = false;
            point    = infoEle.indexOf(",");
            if(point>0) {
                infoList = BaseUtil.splitToList(infoEle,",");
            }else {
                infoList = new ArrayList<String>();
                infoList.add(infoEle);
            }
            for(String ele:infoList) {
                point = ele.indexOf("-");
                if(point>0) {
                    sInfo = sint(ele.substring(0,point));
                    eInfo = sint(ele.substring(point+1));
                    if(sInfo<0 || sInfo>59 || eInfo<0 || eInfo>59 || sInfo>eInfo) {
                        warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:101",null);
                        return false;
                    }
                    //原本是小于等于eInfo 改为小于，因为如果等于，就是超过这个结束时间时才结束  比如：1-30,
                    //按照理解，到了30分时就结束了。但实际上到了31分才结束，所以用小于
                    for(int i=sInfo;i<=eInfo;i++) {
                        if(!minuteList.contains(i)) {
                            minuteList.add(i);
                        }
                        if(!noSecInfo) {
                        	notIncLastMinute = i;
                        }
                    }
                }else {
                    sInfo = sint(ele);
                    if(sInfo>-1 && sInfo<60) {
                    	minuteList.add(sInfo);
                    }
                }
            }
        }
        
        /*
         * 处理小时
         */
        if(!(infoStrList.size()>infoStrPoint)) {
            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:111",null);
        }
        infoEle = infoStrList.get(infoStrPoint++);
        point = infoEle.indexOf("/");
        if(point>0) {
        	disabled     = false;
            intervalHour = sint(infoEle.substring(point+1));
            infoEle      = infoEle.substring(0,point);
        }
        if(!"*".equals(infoEle)) {
        	disabled = false;
            point    = infoEle.indexOf(",");
            if(point>0) {
                infoList = BaseUtil.splitToList(infoEle,",");
            }else {
                infoList = new ArrayList<String>();
                infoList.add(infoEle);
            }
            for(String ele:infoList) {
                point = ele.indexOf("-");
                if(point>0) {
                    sInfo = sint(ele.substring(0,point));
                    eInfo = sint(ele.substring(point+1));
                    if(sInfo<0 || sInfo>59 || eInfo<0 || eInfo>59 || sInfo>eInfo) {
                        warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:103",null);
                        return false;
                    }
                    //原本是小于等于eInfo 改为小于，因为如果等于，就是超过这个结束时间时才结束  比如：9-15,
                    //按照理解，到了15点就结束了。但实际上到了16点才结束，所以用小于
                    for(int i=sInfo;i<=eInfo;i++) {
                        if(!hourList.contains(i)) {
                            hourList.add(i);
                        }
                        notIncLastHour = i;
                    }
                }else {
                    sInfo = sint(ele);
                    if(sInfo>-1 && sInfo<60) {
                    	hourList.add(sInfo);
                    }
                }
            }
        }

        /*
         * 处理日
         */
        if(!(infoStrList.size()>infoStrPoint)) {
            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:111",null);
        }
        infoEle = infoStrList.get(infoStrPoint++);
        point   = infoEle.indexOf("/");
        if(point>0) {
        	disabled    = false;
            intervalDay = SInteger.valueOf(infoEle.substring(point+1));
            infoEle     = infoEle.substring(0,point);
        }
        if(!"*".equals(infoEle)) {
        	disabled = false;
            point    = infoEle.indexOf(",");
            if(point>0) {
                infoList = BaseUtil.splitToList(infoEle,",");
            }else {
                infoList = new ArrayList<String>();
                infoList.add(infoEle);
            }
            for(String ele:infoList) {
                point = ele.indexOf("-");
                if(point>0) {
                    sInfo = sint(ele.substring(0,point));
                    eInfo = sint(ele.substring(point+1));
                    if(sInfo<1 || sInfo>31 || eInfo<1 || eInfo>31 || sInfo>eInfo) {
                        warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:105",null);
                        return false;
                    }
                    //原本是小于等于eInfo 改为小于，因为如果等于，就是超过这个结束时间时才结束  比如：1-15,
                    //按照理解，到了15号就结束了。但实际上到了16号才结束，所以用小于
                    for(int i=sInfo;i<=eInfo;i++) {
                        if(!dayList.contains(i)) {
                            dayList.add(i);
                        }
                    }
                }else {
                	if(infoList.size()>1) {
                		//多个值时，每个值元素不应该有后缀
                		sInfo = sint(ele);
                	}else {
                		//只有一个值时，可能存在后缀 W 或 L
                		ele = ele.toUpperCase();
                		if(ele.indexOf("W")>0) {
                			workDayMode = true;
                		}
                		if(ele.indexOf("L")>0) {
                			lastDayMode = true;
                		}
                		//提取数数字值
                		sInfo = StringUtil.getIntFromString(ele);
                	}
                    if(sInfo>0 && sInfo<32) {
                    	dayList.add(sInfo);
                    }
                }
            }
        }
        
        /*
         * 处理月
         */
        if(!(infoStrList.size()>infoStrPoint)) {
            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:111",null);
        }
        infoEle = infoStrList.get(infoStrPoint++);
        point   = infoEle.indexOf("/");
        if(point>0) {
        	disabled      = false;
        	intervalMonth = sint(infoEle.substring(point+1));
            infoEle       = infoEle.substring(0,point);
        }
        if(!"*".equals(infoEle)) {
        	disabled = false;
            point    = infoEle.indexOf(",");
            if(point>0) {
                infoList = BaseUtil.splitToList(infoEle,",");
            }else {
                infoList = new ArrayList<String>();
                infoList.add(infoEle);
            }
            for(String ele:infoList) {
                point = ele.indexOf("-");
                if(point>0) {
                    info = ele.substring(0,point);
                    sInfo = sint(info);
                    if(sInfo==0) {
                        sInfo = getMonthNameList().indexOf(info.toLowerCase());
                    }
                    info  = ele.substring(point+1);
                    eInfo = sint(info);
                    if(eInfo==0) {
                        eInfo = getMonthNameList().indexOf(info.toLowerCase());
                    }
                    if(sInfo<1 || sInfo>12 || eInfo<1 || eInfo>12 || sInfo>eInfo) {
                        warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:107",null);
                        return false;
                    }
                    //原本是小于等于eInfo 改为小于，因为如果等于，就是超过这个结束时间时才结束  比如：1-6,
                    //按照理解，到了6月时就结束了。但实际上到了7月才结束，所以用小于
                    for(int i=sInfo;i<=eInfo;i++) {
                        if(!monthList.contains(i)) {
                            monthList.add(i);
                        }
                    }
                }else {
                    sInfo = sint(ele);
                    if(sInfo==0) {
                        sInfo = getMonthNameList().indexOf(ele.toLowerCase());
                    }
                    if(sInfo>0 && sInfo<13) {
                    	monthList.add(sInfo);
                    }
                }
            }
        }
        

        /*
         * 处理周
         */
        if(!(infoStrList.size()>infoStrPoint)) {
            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:111",null);
        }
        infoEle = infoStrList.get(infoStrPoint++);
        point = infoEle.indexOf("/");
        if(point>0) {
        	disabled     = false;
        	intervalWeek = sint(infoEle.substring(point+1));
            infoEle      = infoEle.substring(0,point);
        }
        if(!"*".equals(infoEle)) {
        	disabled = false;
            point    = infoEle.indexOf(",");
            if(point>0) {
                infoList = BaseUtil.splitToList(infoEle,",");
            }else {
                infoList = new ArrayList<String>();
                infoList.add(infoEle);
            }
            for(String ele:infoList) {
                point = ele.indexOf("-");
                if(point>0) {
                    info = ele.substring(0,point);
                    sInfo = sint(info);
                    if(sInfo==0 && !"0".equals(info)) {
                        sInfo = getWeekNameList().indexOf(info.toLowerCase());
                    }else if(sInfo==0) {
                    	sInfo = 7; //0也是周日，改为7
                    }
                    info = ele.substring(point+1);
                    eInfo = sint(info);
                    if(eInfo==0 && !"0".equals(info)) {
                        eInfo = getWeekNameList().indexOf(info.toLowerCase());
                    }else if(eInfo==0) {
                    	eInfo = 7; //0也是周日，改为7
                    }
                    if(sInfo<1 || sInfo>7 || eInfo<1 || eInfo>7 || sInfo>eInfo) {
                        warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:109",null);
                        return false;
                    }
                    for(int i=sInfo;i<=eInfo;i++) {
                        if(!weekList.contains(i)) {
                            weekList.add(i);
                        }
                    }
                }else {
                	sInfo = getWeekNameList().indexOf(ele.toLowerCase());
                	if(sInfo<1) {
                		if(ele.toUpperCase().indexOf("L")>0) {
                			lastWeekMode = true;
                		}
                		sInfo = StringUtil.getIntFromString(ele);
                	}
                	if(sInfo==0) {
                		sInfo = 7; //0也是周日，改为7
                	}
                    if(sInfo>0 && sInfo<8) {
                    	weekList.add(sInfo);
                    }
                }
            }
        }

        /*
         * 处理年信息
         */
        if(infoStrList.size()>6) {
        	disabled = false;
            infoEle  = infoStrList.get(infoStrPoint++);
            point    = infoEle.indexOf("/");
            if(point>0) {
                intervalYear = SInteger.valueOf(infoEle.substring(point+1));
                infoEle = infoEle.substring(0,point);
            }
            if(!"*".equals(infoEle)) {
            	disabled = false;
                point    = infoEle.indexOf(",");
                if(point>0) {
                    infoList = BaseUtil.splitToList(infoEle,",");
                }else {
                    infoList = new ArrayList<String>();
                    infoList.add(infoEle);
                }
                for(String ele:infoList) {
                    point = ele.indexOf("-");
                    if(point>0) {
                        sInfo = sint(ele.substring(0,point));
                        eInfo = sint(ele.substring(point+1));
                        if(sInfo<0 || sInfo>59 || eInfo<0 || eInfo>59 || sInfo>eInfo) {
                            warning("The Crontab ["+getName()+"] Time Info ["+timeInfo+"] Error Code:103",null);
                            return false;
                        }
                        //原本是小于等于eInfo 改为小于，因为如果等于，就是超过这个结束时间时才结束  比如：9-15,
                        //按照理解，到了15点就结束了。但实际上到了16点才结束，所以用小于
                        for(int i=sInfo;i<=eInfo;i++) {
                            if(!yearList.contains(i)) {
                            	yearList.add(i);
                            }
                        }
                    }else {
                        sInfo = sint(ele);
                        yearList.add(sInfo);
                    }
                }
            }
        }
        return true;
    }
    
    /**
     * 去掉多余的空格
     * @param info 待处理字符串
     * @return 处理后的字符串
     * 2016年6月13日
     * @author MBG
     */
    private String fixTimeInfo(String info) {
    	if(info==null) {
    		return "";
    	}
    	info = BaseUtil.swapString(info,"\t"," ");
    	while(info.indexOf("  ")>-1) {
    		info = BaseUtil.swapString(info,"  "," ");
    	}
    	return info;
    }
    
    /**
     * 是否到点
     * @return 是否到点
     * 2014年7月4日
     * @author 马宝刚
     */
    public boolean onTime() {
    	if(isRunning) {
    		return false;
    	}
        //获取下个执行时间
        if(nextDate==null) {
        	nextDate     = sNextOnTime();
        	nextDateLong = nextDate.getMillisecond();
        	return false;
        }
        if(nextDateLong>System.currentTimeMillis()){
        	//未到时间，或者刚到时间但已经执行完了
        	return false;
        }
        lastDate     = nextDate;
        lastDateLong = nextDateLong;
        
        nextDate     = sNextOnTime();
        nextDateLong = nextDate.getMillisecond();
        //下一个值还是当前值，说明已经执行过
        return nextDateLong != lastDateLong;
    }
    
    /**
     * 获取序列号
     * @return 序列号
     * 2015年11月23日
     * @author 马宝刚
     */
    private String getSID() {
        if(sn==null) {
        	sn = FSN.newInstance("_crontab_",40);
        }
        return sn.getSID();
    }
    
    /**
     * 设置工作日日期信息序列  日期格式：yyyy-mm-dd
     * @param workDayList 工作日日期序列
     * 2018年6月2日
     * @author MBG
     */
    public void setWorkDayList(List<String> workDayList) {
    	this.workDayList = workDayList;
    }
    
    /**
     * 获取有效的工作日日期信息序列  日期格式：yyyy-mm-dd
     * @return 有效的工作日日期信息序列
     * 2018年6月2日
     * @author MBG
     */
    public List<String> getWorkDayList() {
    	if(workDayList==null) {
    		workDayList = new ArrayList<String>();
    	}
    	return workDayList;
    }
    
    /**
     * 设置非工作日日期信息序列  日期格式：yyyy-mm-dd
     * @param unWorkDayList 非工作日日期信息序列
     * 2018年6月2日
     * @author MBG
     */
    public void setUnWorkDayList(List<String> unWorkDayList) {
    	this.unWorkDayList = unWorkDayList;
    }
    
    /**
     * 获取有效的非工作日日期信息序列 日期格式：yyyy-mm-dd
     * @return 非工作日日期信息序列
     * 2018年6月2日
     * @author MBG
     */
    public List<String> getUnWorkDayList(){
    	if(unWorkDayList==null) {
    		unWorkDayList = new ArrayList<String>();
    	}
    	return unWorkDayList;
    }
    
    /**
     * 返回下一个执行时间对象
     * @return 下一个执行时间对象
     * 2018年6月12日
     * @author MBG
     */
    public SDate nextTime() {
    	testDate = sNextOnTime(testDate);
    	return (SDate)testDate.clone();
    }
    
    /**
     * 获取下一个执行时间对象
     * @param curDate 当前时间对象
     * @return 下一个执行时间对象 （返回空，没有下次执行时间）
     * 2018年5月23日
     * @author MBG
     */
    public SDate sNextOnTime(SDate curDate) {
    	if(disabled) {
    		//返回无限大值（并不是最无限大，避免对返回值做累加处理导致溢出）
    		return new SDate("9999-12-30 00:00:00");
    	}
    	SDate resDate; //构建返回对象
    	if(curDate==null) {
    		curDate = new SDate();
    		resDate = new SDate();
    	}else {
    		resDate = (SDate)curDate.clone();
    	}
    	/*
    	 * 需要考虑到的情况：
    	 * 
    	 * 1. 秒值到时间了，分值以后还没到时间
    	 * 2. 秒值没到时间，分值到时间了，分值以后没到时间
    	 * 3. 秒、分没到时间，
    	 * 4. 秒、分、小时都到时间了（做了进位或设置了下一次执行的值）但是天值也
    	 */
    	
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		Integer   upStep           = null;  //进位值
    	int       value            = 0;     //判断进位值
    	                           
    	Integer   minSec           = null;  //秒范围最小值
    	Integer   minMinute        = null;  //分钟范围最小值
    	Integer   minHour          = null;  //小时范围最小值
    	Integer   minDay           = null;  //日范围最小值
    	Integer   minWeek          = null;  //周范围最小值
    	Integer   minMonth         = null;  //月份范围最小值
    	                           
    	boolean   secChanged       = false; //秒是否发生了变化
    	boolean   minuteChanged    = false; //分钟是否发生了变化
    	boolean   hourChanged      = false; //小时是否发生了变化
    	boolean   dayChanged       = false; //天是否发生了变化
    	boolean   monthChanged     = false; //月份是否发生了变化
    	                           
    	boolean   resetSec         = false; //是否重置秒
    	boolean   resetMinute      = false; //是否重置分
    	boolean   resetHour        = false; //是否重置小时
    	boolean   resetDay         = false; //是否重置天
    	boolean   resetMonth       = false; //是否重置月份
    	boolean   setDayAfterMonth = false; //处理完月份后再处理日期
    	
        /* ** 处理秒信息 ** */
    	int upSec = resDate.getSecond(); //改变前的秒值
    	if(secList.size()==0) {
    		if(intervalSec<1) {
    			upStep     = 0;        //设置进位值（标记没有秒信息）
        		resDate.setSecond(0);  //设置整数秒触发 
    		}else {
    			value = curDate.getSecond()+intervalSec;
    			if(value>59) {
    				upStep = value/60;
    			}
    			resDate.setSecond(value);
    		}
    	}else if(secList.size()==1) {
        	
        	int curSec = resDate.getSecond(); //当前秒值
        	int objSec = secList.get(0);      //目标秒值
        	
        	if(intervalSec>0) {
    			/* ** 如果当前秒值过了目标秒值 就在当前秒值基础上加上间隔秒值 ** */
        		if(curSec<objSec) {
        			resDate.setSecond(objSec);
        		}else {
        			if(curSec%intervalSec>0) {
        				value = intervalSec*(curSec/intervalSec+1);
        				resDate.setSecond(value);
        				if(value>59) {
        					upStep = value/60;
        				}
        			}else {
        				value = curSec+intervalSec;
        				if(value>59) {
        					upStep = value/60;
        					value  = objSec;
        				}
        				resDate.setSecond(value);
        			}
        		}
    		}else {
        		/* ** 没设置间隔值 ** */
        		if(curSec>=objSec) {
        			/* ** 已经过了目标秒值 ** */
        			upStep = 1;  //标记需要做进位处理
        		}
        		resDate.setSecond(objSec);
        	}
        }else if(secList.size()>1) {
        	
        	/* ** 设置了秒值枚举值 ** */
        	
        	int curSec = resDate.getSecond();  //当前秒值
        	int maxSec = 0; //范围中，最大秒值
    		for(int ele:secList) {
    			if(minSec==null || minSec>ele) {
    				minSec = ele;
    			}
    			if(maxSec<ele) {
    				maxSec = ele;
    			}
    		}
        	if(intervalSec>0) {
        		curSec += intervalSec;
        	}else {
        		curSec++; //累加下一秒
        	}
    		int objSec  = -1; //比当前秒稍大一点的值
    		for(int ele:secList) {
    			if(curSec>ele) {
    				continue;
    			}
    			if(objSec<0 || objSec>ele) {
    				objSec = ele;
    			}
    		}
    		if(objSec<0) {
    			objSec = minSec;
    		}
    		if(curSec<=objSec) {
    			/* ** 在范围中找到比当前秒大的值 直接设置范围内的值 ** */
    			resDate.setSecond(objSec);
    		}else {
    			//在范围中没有匹配到累加间隔秒后的值，于是标记分钟累加1，然后将范围中最小值设置为当前秒
    			upStep = 1; //标记需要做进位处理
    			resDate.setSecond(minSec);
    		}
        }else if(intervalSec>0) {
        	/* ** 没有设置秒枚举值，但是设置了间隔秒值 ** */
        	value = resDate.getSecond()+intervalSec;
        	if(value>59) {
        		upStep = 1;
        	}
        	resDate.setSecondApoint(value);
        }
        // else {
          /* ** secList.size() 不存在小于0 ** */
        // }
    	if(upSec!=resDate.getSecond() || (upStep!=null && upStep>0)) {
    		secChanged = true;
    	}
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /* ** 处理分钟信息 ** */
        
        if(upStep!=null || minuteList.size()>0) {
        	
        	int iStep    = upStep==null?0:upStep; //本段进位值
        	int upMinute = resDate.getMinute(); //处理前的分钟分钟值
        	upStep       = null;   //清空用作下一段进位值
        	if(minuteList.size()<1) {
        		if(iStep>0) {
        			/* ** 已经设置了秒信息，并且需要分钟进位。要么从当前分钟值设置间隔时间，要么分钟进1 iStep肯定大于等于1 ** */
        			if(intervalMinute>iStep) {
        				iStep = intervalMinute;
        			}
    				value = upMinute+iStep;
    				if(value>59) {
    					upStep = value/60;
    				}
    				resDate.setMinute(value);
        		}else {
        			if(intervalMinute>0) {
        				value = upMinute+intervalMinute;
        				if(value>59) {
        					upStep = value/60;
        				}
        				resDate.setMinute(value);
        			}else {
                		upStep     = 0;       //没有设置分钟间隔信息
                		resDate.setMinute(0); //设置为0分钟（整点）
        			}
        		}
        	}else if(minuteList.size()==1) {
            	
            	/* ** 设定分钟值 只有指定了小时值，指定分钟值才有效 ** */
            	
            	int curMinute = upMinute+iStep; //当前分钟值
            	int objMinute = minuteList.get(0);         //目标分钟值
            	
            	if(intervalMinute>0) {
            		if(curMinute<objMinute) {
            			resDate.setMinute(objMinute);
            		}else {
                		/* ** 已经超过规定分钟值，直接累加分钟间隔值 ** */
            			if(curMinute%intervalMinute>0) {
            				//获取计算后的值
            				value = intervalMinute*(curMinute/intervalMinute+1);
            				resDate.setMinute(value);
            				if(value>59) {
            					upStep = value/60;
            				}
            			}else {
            				value = curMinute+intervalMinute;
            				if(value>59) {
            					upStep = value/60;
            					value = objMinute;
            				}
            				resDate.setMinute(value);
            			}
            		}
            	}else {
            		/* ** 没设置间隔值 ** */
            		if(curMinute>objMinute || (curMinute==objMinute && !secChanged)) {
            			/* ** 已经过了目标分钟值，小时值进1 ** */
            			upStep = 1; //标记需要进位
            		}
            		resDate.setMinute(objMinute);
            	}
            }else{
            	/* ** 存在分钟枚举值（限定了分钟值的范围） ** */
            	int maxMinute = 0; //范围中，最大分钟值
    			for(int ele:minuteList) {
    				if(minMinute==null || minMinute>ele) {
    					minMinute = ele;
    				}
    				if(maxMinute<ele) {
    					maxMinute = ele;
    				}
    			}
    			int curMinute = upMinute; //预设值的当前值
            	if(intervalMinute>0) {
            		curMinute += intervalMinute>iStep?intervalMinute:iStep; //下个触发分钟值
            	}else{
            		curMinute += iStep;
            	}
        		if(curMinute>59) {
        			upStep    = curMinute/60;
        			curMinute = curMinute%60;
        		}
    			int objMinute   = -1; //比触发月份大一些的分钟
    			//注意，序列中的分钟数不一定是由小到大的
    			for(int ele:minuteList) {
    				if(((curMinute<ele && intervalMinute<1) || ((secChanged || iStep>0 || noSecInfo) && curMinute==ele)) && (objMinute<0 || objMinute>ele || curMinute==objMinute)) {
    					if(notIncLastMinute!=null && notIncLastMinute==ele && iStep<1 && !noSecInfo) {
        					if(notIncLastMinute!=upMinute && resDate.getSecond()<1) {
        						objMinute = ele;
        					}
    					}else {
    						objMinute = ele;
    					}
    				}
    			}
    			if(objMinute<0) {
    				objMinute = minMinute;
    			}
    			if(curMinute>objMinute) {
    				resDate.setMinute(minMinute);
    				if(upStep==null) {
    					upStep = 1;
    				}else {
    					upStep++;
    				}
    			}else {
    				resDate.setMinute(objMinute);
    			}
            }
        	if(upMinute!=resDate.getMinute() || (upStep!=null && upStep>0)) {
        		minuteChanged = true;
        		if(iStep<1 || (upStep!=null && upStep>0 && minuteList.size()>0)) {
        			resetSec = true;
        		}
        	}
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        /* ** 处理小时信息 ** */
        
        if(upStep!=null || hourList.size()>0) {
        	
        	int iStep  = upStep==null?0:upStep; //本段进位值
        	int upHour = resDate.getHour();     //处理前的小时值
        	upStep     = null;                  //清空用作下一段进位值

        	if(hourList.size()<1) {
        		if(iStep>0) {
        			/* ** 已经设置了分钟或秒信息，并且需要进位。要么从当前值设置间隔时间，要么进1 ** */
        			if(intervalHour>iStep) {
        				iStep = intervalHour;
        			}
    				value = upHour+iStep;
        			if(value>23) {
        				upStep = value/24;
        			}
        			resDate.setHour(value);
        		} else {
        			if(intervalHour>0) {
        				value = upHour+intervalHour;
        				if(value>23) {
        					upStep = value/24;
        				}
        				resDate.setHour(value);
        			}else {
        				upStep = 0;           //没有设置小时信息
        				resDate.setHour(0);   //设置为0点执行
        			}
        		}
        	}else if(hourList.size()==1) {
            	
            	/* ** 指定了触发小时值 ** */
            	
            	int curHour = upHour+iStep;  //当前小时值
            	int objHour = hourList.get(0);          //指定小时值
            	
            	if(intervalHour>0) {
            		if(curHour<objHour) {
            			resDate.setHour(objHour);
            		}else {
            			if(curHour%intervalHour>0) {
            				//计算后的值
            				value = intervalHour*(curHour/intervalHour+1);
            				resDate.setHour(value);
            				if(value>23) {
            					upStep = value/24;
            				}
            			}else {
            				value = curHour+intervalHour;
            				if(value>23) {
            					upStep = value/24;
            					value  = objHour;
            				}
            				resDate.setHour(value);
            			}
            		}
        		}else {
            		/* ** 没有设置间隔小时值 ** */
            		if(curHour>objHour || (curHour==objHour && !(secChanged || minuteChanged))) {
            			/* ** 过了目标小时值，天值进位1 ** */
            			upStep = 1; //标记需要进位处理
            		}
            		resDate.setHour(objHour);
            	}
            } else {

            	/* ** 存在枚举小时值（小时值范围） ** */
            	int maxHour = 0; //范围中，最大小时值
    			for(int ele:hourList) {
    				if(minHour==null || minHour>ele) {
    					minHour = ele;
    				}
    				if(maxHour<ele) {
    					maxHour = ele;
    				}
    			}
    			int curHour = upHour; //当前值
            	if(intervalHour>0) {
            		curHour += intervalHour>iStep?intervalHour:iStep; //下个触发小时值
            	}else{
            		curHour += iStep;
            	}
            	if(curHour>23) {
        			upStep  = curHour/24;
        			curHour = curHour%24;
        		}
    			int objHour   = -1;
    			for(int ele:hourList) {
        			if(((curHour<ele && intervalHour<1) || ((minuteChanged || secChanged || iStep>0) && curHour==ele)) && (objHour<0 || objHour>ele)) {
        				if(notIncLastHour!=null && notIncLastHour==ele) {
        					if(notIncLastHour!=upHour && resDate.getMinute()<1 && resDate.getSecond()<1) {
        						objHour = ele;
        					}
        				}else {
        					objHour = ele;
        				}
        			}
    			}
    			if(objHour<0) {
    				objHour = minHour;
    			}
    			if(curHour>objHour) {
    				resDate.setHour(minHour);
    				if(upStep==null) {
    					upStep = 1;
    				}else {
    					upStep++;
    				}
    			}else {
    				resDate.setHour(objHour);
    			}
            }
        	if(upHour!=resDate.getHour() || (upStep!=null && upStep>0)) {
        		hourChanged = true;
        		if(iStep<1 || (upStep!=null && upStep>0 && hourList.size()>0)) {
        			resetMinute = true;
        		}
        	}
        }
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        
        /*
         * 处理周信息和天信息
         * 
         * 注意：周跟天不能同时使用，这也是不合理的定时信息
         * 
         * 程序考虑到容错性，允许错误的定时信息也能执行。
         * 
         * 如果存在天信息，就不再解析周信息（因为周信息也是天信息的一种）
         * 
         * 只要存在周信息值，就是按照7天循环处理的周期
         * 类似于设定没7天周期处理事件
         */
        if(upStep!=null || dayList.size()>0 || weekList.size()>0) {
        	
        	int iStep  = upStep==null?0:upStep;      //本段进位值
        	int mDay   = resDate.getThisMonthDays(); //当月总共天数
        	int upDay  = resDate.getDay();           //处理前的天值
        	upStep     = null;                       //清空用作下一段进位值
        	
        	if(dayList.size()<1 && weekList.size()<1) {
        		
    			int iDay = 0; //间隔天数
    			if(intervalDay>0) {
    				iDay = intervalDay;
    			}else if(intervalWeek>0) {
    				iDay = 7*intervalWeek;
    			}
        		if(iStep>0) {
        			if(iDay>iStep) {
        				iStep = iDay;
        			}
        			value = upDay+iStep;
        			if(value>resDate.getThisMonthDays()) {
        				upStep = SDate.calcMonth(value,resDate.getMonth(),resDate.getYear());
        			}
        			resDate.setDay(value);
        		}else {
        			if(iDay>0) {
            			value = upDay+iDay;
            			if(value>resDate.getThisMonthDays()) {
            				upStep = SDate.calcMonth(value,resDate.getMonth(),resDate.getYear());
            			}
            			resDate.setDay(value);
        			}else {
            			upStep = 0;          //标记没设置日期，星期值，也没设置小时，分秒值
            			resDate.setDay(1);   //默认为1号
        			}
        		}
        	}else if(dayList.size()==1) {
            	
            	/* ** 处理指定天值信息 处理后，就不再处理天间隔值 ** */
            	
            	int curDay = upDay+iStep;     //当前天值
            	int objDay = dayList.get(0);             //目标天值
            	if(lastDayMode) {
            		//当月倒数天数模式
            		objDay = resDate.getThisMonthDays()-objDay+1;
            		if(curDay>=objDay) {
            			upStep = 1;
            			setDayAfterMonth = true; //假如当月是9月份，而最终结果需要是10月31日，那只能在设置完月份以后再设置日期
            		}
            		resDate.setDay(objDay);
            	}else {
            		if(intervalDay>0) {
                		if(curDay<objDay) {
                			resDate.setDay(objDay);
                		}else {
                			if(curDay%intervalDay>0) {
                				value = intervalDay*(curDay/intervalDay+1);
                				resDate.setDay(value);
                				if(value>mDay) {
                					upStep = SDate.calcMonth(value,resDate.getMonth(),resDate.getYear());
                				}
                			}else {
                				value = curDay+intervalDay;
                				if(value>mDay) {
                					upStep = SDate.calcMonth(value,resDate.getMonth(),resDate.getYear());
                					value = objDay;
                				}
                				resDate.setDay(value);
                			}
                		}
            		} else {
                		/* ** 没有设置间隔天值 ** */
                		if(curDay>objDay || (curDay==objDay && !(secChanged || minuteChanged || hourChanged))) {
                			/* ** 已经过了目标天值，月份值进1 ** */
                			upStep = 1;
                		}
            			resDate.setDay(objDay);
                	}
            	}
            }else if(dayList.size()>1) {
            	/*
            	 * 即存在天枚举信息，又存在间隔天数信息
            	 * 比如：* * 1-20/2 * *  
            	 * 即：1号到20号期间，每隔两天执行一次
            	 */
            	int maxDay = 1; //范围中，最大天值
    			for(int ele:dayList) {
    				if(minDay==null || minDay>ele) {
    					minDay = ele;
    				}
    				if(maxDay<ele) {
    					maxDay = ele;
    				}
    			}
    			int curDay = upDay; //当前值
            	if(intervalDay>0) {
            		curDay += intervalDay>iStep?intervalDay:iStep; //下个触发天值
            	}else{
            		curDay += iStep;
            	}
        		if(curDay>mDay) {
        			upStep = SDate.calcMonth(curDay,resDate.getMonth(),resDate.getYear());
        			curDay = resDate.setDayApoint(iStep).getDay();
        			resDate.setDayApoint(0-iStep);
        		}
    			int objDay = -1; //比触发天大一些的天值
    			//注意，序列中的天数不一定是由小到大的（奇葩设置）
    			for(int ele:dayList) {
        			if(((curDay<ele && intervalDay<1) || ((secChanged || minuteChanged || hourChanged || iStep>0) && curDay==ele)) && (objDay<0 || objDay>ele)) {
        				objDay = ele;
        			}
    			}
    			if(objDay<0) {
    				objDay = minDay;
    			}
    			if(curDay<=objDay) {
    				/* ** 设置范围内下一个天值 ** */
    				resDate.setDay(objDay);
    			}else {
    				/* ** 不在天值范围内，则设定为下个月的最小的天值范围 ** */
    				resDate.setDay(minDay);
    				if(upStep==null) {
    					upStep = 1;
    				}else {
    					upStep++;
    				}
    			}
            }else if(intervalDay>0) {
            	
            	/* ** 只设置了间隔天数 ** */
            	
            	resDate.setDayApoint(intervalDay);
            	
            	/*
            	 * 注意：如果又设置了间隔天数，又设置了间隔几周，那么间隔几周将不起作用
            	 *       本来也不应该这么设置。
            	 */
            	
            	if(weekList.size()>1) {
            		
            		/* ** 间隔天，但是也做了周限制 ** */
            		
            		/* ** 没有设置周间隔，但是有周范围 ** */
            		int maxWeek = 1; //范围中，最大星期值
            		for(int ele:weekList) {
        				if(minWeek==null || minWeek>ele) {
        					minWeek = ele;
        				}
        				if(maxWeek<ele) {
        					maxWeek = ele;
        				}
            		}
            	 	//设置为第二日，然后获取周几
                	int curWeek = resDate.getDayOfWeek();
        			int objWeek   = -1; //比触发周几大一些的天值
        			//注意，序列中的周几数不一定是由小到大的（奇葩设置）
        			for(int ele:weekList) {
            			if(((curWeek<ele && intervalWeek<1) || ((secChanged || minuteChanged || hourChanged || iStep>0) && curWeek==ele))&& (objWeek<0 || objWeek>ele)) {
            				objWeek = ele;
            			}
        			}
        			if(objWeek<0) {
        				objWeek = minWeek;
        			}
        			int objDay = upDay;
        			if(curWeek<=objWeek) {
        				//假如当前是星期一，而范围中最小值是星期三，直接跳转到星期三
        				objDay += objWeek-curWeek;
        			}else {
        				//假如当前是星期五，范围是 周二到周五，则直接跳到下周二
        				objDay += 7-curWeek+minWeek;
        			}
        			if(objDay>mDay) {
            			upStep = SDate.calcMonth(objDay,resDate.getMonth(),resDate.getYear());
        			}
        			resDate.setDay(objDay);
            	}
            	
            }else if(weekList.size()==1) {
            	
            	/* ** 没有设置天值，但是设置了周值 ** */
            	
            	int curWeek = resDate.setDayApoint(iStep).getDayOfWeek(); //当前星期值
            	int objWeek = weekList.get(0); //目标周值
            			
            	if(lastWeekMode) {
            		if(resDate.isLastWeek()) {
            			if(curWeek>objWeek || (curWeek==objWeek && !(secChanged || minuteChanged || hourChanged))) {
                			//这是上一个执行日期（已经过期）
                			upStep = 1;
                			setDayAfterMonth = true; //假如当月是9月份，而最终结果需要是10月31日，那只能在设置完月份以后再设置日期
            			}else {
            				resDate.setDayApoint(objWeek-curWeek);
            			}
            		}else {
            			//当月，还没到末尾周，指定周几
            			resDate.setDay(SDate.getMonthLastWeekDay(objWeek,resDate.getMonth(),resDate.getYear()));
            		}
            	}else {
                	int objDay = resDate.getDay(); //目标天
                	if(intervalWeek>0) {
                		if(curWeek<objWeek) {
                			/* ** 没到指定的周值 ** */
                    		//直接加几天到指定周值
                			objDay +=objWeek-curWeek;
                    	}else if(curWeek==objWeek) {
                    		//已经过了执行时间，直接进入下一个周期
                    		objDay += 7*intervalWeek;
                    	}else {
                    		//当前周值大于指定周值，进入下一个周期的指定周值
                    		objDay += (intervalWeek-1)+(7-curWeek+objWeek);
                    	}
                	}else {
                		if(curWeek<=objWeek) {
                    		//直接加几天到指定周值
                			objDay += objWeek-curWeek;
                    	}else if(curWeek==objWeek) {
                    		//已经过了执行时间，直接进入下一个周期
                    		objDay += 7;
                    	}else {
                    		//当前周值大于指定周值，进入下一周的指定周值
                    		objDay += 7-curWeek+objWeek;
                    	}
                	}
        			if(objDay>mDay) {
            			upStep = SDate.calcMonth(objDay,resDate.getMonth(),resDate.getYear());
        			}
        			resDate.setDay(objDay);
            	}
            }else if(weekList.size()>1) {
            	
            	/*
            	 * 可以这么写：  * * * * 1-5
            	 * 即：周一到周五执行
            	 * 
            	 * 可以这么写:   * * * * 3 
            	 * 即：每周三执行
            	 * 
            	 * 也可以这么写： * * * * * /3
            	 * 即：间隔3周再执行 （但很少这么写）(* /3 中间没有空格，这里只能写空格，否则注释报错）
            	 */
            	int maxWeek = 0; //范围中，最大星期值
        		for(int ele:weekList) {
    				if(minWeek==null || minWeek>ele) {
    					minWeek = ele;
    				}
    				if(maxWeek<ele) {
    					maxWeek = ele;
    				}
        		}
            	
        		int curWeek; //目标周几
            	if(intervalWeek>0) {
            		//一周即间隔7天  间隔多少周
            		resDate.setDayApoint((iStep>intervalWeek?iStep:intervalWeek)*7);
                   	//获取周几
            		curWeek = resDate.getDayOfWeek();
            	}else {
            	 	//设置为第二日，然后获取周几
                	curWeek = resDate.setDayApoint(iStep).getDayOfWeek();
            	}
            	int objWeek   = -1; //比触发周几大一些的天值
    			//注意，序列中的周几数不一定是由小到大的
    			for(int ele:weekList) {
        			if(((curWeek<ele && intervalWeek<1) || curWeek==ele) && (objWeek<0 || objWeek>ele)) {
        				objWeek = ele;
        			}
    			}
    			if(objWeek<0) {
    				objWeek = minWeek;
    			}
    			int objDay = resDate.getDay(); //目标天数
    			if(curWeek<=objWeek) {
    				//假如当前是星期一，而范围中下一个值是星期三，直接跳转到星期三
    				objDay += objWeek-curWeek;
    			}else {
					//假如当前是星期五，范围是 周二到周五，则直接跳到下周二
    				objDay += 7-curWeek+minWeek;
    			}
    			if(objDay>mDay) {
        			upStep = SDate.calcMonth(objDay,resDate.getMonth(),resDate.getYear());
    			}
    			resDate.setDay(objDay);
            }else if(intervalWeek>0) {
            	/* ** 设置了间隔周 但没有周范围**/
            	int objDay = upDay+(intervalWeek*7);
    			if(objDay>mDay) {
        			upStep = SDate.calcMonth(objDay,resDate.getMonth(),resDate.getYear());
    			}
    			resDate.setDay(objDay);
            }
        	if(upDay!=resDate.getDay() || (upStep!=null && upStep>0)) {
        		dayChanged = true;
        		if(iStep<1 || (upStep!=null && upStep>0 && (dayList.size()>0 || weekList.size()>0))) {
        			resetHour = true;
        		}
        	}
        }

        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        
        /* ** 处理月份数据 ** */
        if(upStep!=null || monthList.size()>0) {
        	
        	int iStep   = upStep==null?0:upStep; //本段进位值
        	int upMonth = resDate.getMonth();    //处理前的月份值
        	upStep      = null;                  //清空用作下一段进位值
        	if(monthList.size()<1) {
        		if(iStep>0) {
        			if(intervalMonth>iStep) {
        				iStep = intervalMonth;
        			}
        			value = upMonth+iStep;
        			if(value>12) {
        				upStep = value/12;
        			}
        			resDate.setMonth(value);
        		}else {
        			if(intervalMonth>0) {
            			value = upMonth+intervalMonth;
            			if(value>12) {
            				upStep = value/12;
            			}
            			resDate.setMonth(value);
        			}else {
        				upStep = 0;              //标记之前的数据都没设置
        				resDate.setMonth(1);     //默认月份为1月
        			}
        		}
        		
        	}else if(monthList.size()==1) {
            	
            	/* ** 处理指定月值信息 ** */
            	
            	int curMonth = upMonth+iStep; //当前月份值
            	int objMonth = monthList.get(0);         //目标月份值
            	
            	if(intervalMonth>0) {
            		if(curMonth<objMonth) {
            			resDate.setMonth(objMonth);
            		}else {
            			if(curMonth%intervalMonth>0) {
            				value = intervalMonth*(curMonth/intervalMonth+1);
            				resDate.setMonth(value);
            				if(value>12) {
            					upStep = value/12;
            				}
            			}else {
            				value = curMonth+intervalMonth;
            				if(value>12) {
            					upStep = value/12;
            					value  = objMonth;
            				}
            				resDate.setMonth(value);
            			}
            		}
            	}else {
            		if(curMonth>objMonth || (curMonth==objMonth && !(secChanged || minuteChanged || hourChanged || dayChanged))) {
            			/* ** 过了指定月份值，年份进1 ** */
            			upStep = 1;
            		}
            		resDate.setMonth(objMonth);
            	}
            } else {
            	int maxMonth = 1; //范围中，最大月份
        		for(int ele:monthList) {
    				if(minMonth==null || minMonth>ele) {
    					minMonth = ele;
    				}
    				if(maxMonth<ele) {
    					maxMonth = ele;
    				}
        		}
        		
        		int curMonth = upMonth; //下一个月份值
        		
            	if(intervalMonth>0) {
            		/* ** 设置了间隔月份值触发 ** */
            		curMonth += intervalMonth>iStep?intervalMonth:iStep; //下个触发月份值
            	}else{
            		/* ** 只设置了触发月份范围 ** */
            		//设置为下一个月
            		curMonth += iStep;
            	}
        		if(curMonth>12) {
        			upStep   = curMonth/12;
        			curMonth = curMonth%12;
        		}
    			int objMonth = -1; //比触发月份大一些的月份
    			for(int ele:monthList) {
        			if(((curMonth<ele && intervalMonth<1) || ((secChanged || minuteChanged || hourChanged || dayChanged || iStep>0) && curMonth==ele)) && (objMonth<0 || objMonth>ele)) {
        				objMonth = ele;
        			}
    			}
    			if(objMonth<0) {
    				objMonth = minMonth;
    			}
    			if(curMonth<=objMonth) {
    				/* ** 存在下一个月份值 直接设置该值 ** */
    				resDate.setMonth(objMonth);
    			}else {
    				/* ** 当前月份值已经超过范围中最大的月份值，年份进1，设置范围中最小的月份值 ** */
    				resDate.setMonth(minMonth);
    				if(upStep==null) {
    					upStep = 1;
    				}else {
    					upStep++;
    				}
    			}
            }
        	if(upMonth!=resDate.getMonth() || (upStep!=null && upStep>0)) {
        		monthChanged = true;
        		if(iStep<1 || (upStep!=null && upStep>0 && monthList.size()>0)) {
        			resetDay = true;
        		}
        	}
        }
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        /* ** 处理年份数据 ** */
        if(upStep!=null || yearList.size()>0) {
        	int iStep  = 0; //年份数据进位值
        	int upYear = resDate.getYear(); //处理前的年份值
        	if(upStep==null) {
        		upStep = 0;
        	}
        	iStep = upStep;
        	if(yearList.size()<1) {
        		if(upStep<intervalYear) {
        			upStep = intervalYear;
        		}
        		resDate.setYearApoint(upStep);
        	}else if(yearList.size()==1) {
            	
            	/* ** 处理指定年值信息 ** */
            	
            	int curYear = upYear+upStep; //当前年份值
            	int objYear = yearList.get(0);          //目标年份值
            	
            	if(intervalYear>0) {
            		/* ** 存在间隔年份值 ** */
            		if(curYear<objYear) {
            			/* ** 还没到指定年份 直接设置目标年份值 ** */
            			resDate.setYear(objYear);
            		}else {
            			/* ** 已经过了目标年份值 累加间隔值 ** */
            			resDate.setYearApoint(intervalYear);
            		}
            	}else {
            		if(curYear<objYear || (iStep==0 && curYear==objYear)) {
            			/* ** 还没到指定年份 或刚到指定年份 ** */
            			resDate.setYear(objYear);
            		}else {
            			/* ** 过了指定年份值，设置个巨大年份，永远也不执行** */
            			resDate.setYear(9999);
            		}
            	}

            }else if(yearList.size()>1) {
            	/*
            	 * 如果超过了年份范围，就不在执行了
            	 * 
            	 * 采用设置年份为9999再执行的方式，不再执行任务
            	 * 
            	 */
            	int minYear = -1; //范围中，最小年份
            	int maxYear = -1; //范围中，最大年份
        		for(int ele:yearList) {
    				if(minYear<0 || minYear>=ele) {
    					minYear = ele;
    				}
    				if(maxYear<0 || maxYear<=ele) {
    					maxYear = ele;
    				}
        		}
        		
        		int curYear; //下一年份值
            	if(intervalYear>0) {
            		/* ** 设置了间隔年份值触发 ** */
            		resDate.setYearApoint(intervalYear);
            		curYear = upYear; //下个触发年份值
            	}else{
            		/* ** 只设置了触发年份范围 ** */
            		//设置为下一年
            		curYear = upYear;
            	}
    			int objYear = -1; //比触发年份大一些的年份
    			//注意，序列中的年份数不一定是由小到大的
    			for(int ele:yearList) {
        			if(((curYear<ele && intervalYear<1) || ((secChanged || minuteChanged || hourChanged || dayChanged || monthChanged || iStep>0) && curYear==ele)) && (objYear<0 || objYear>ele)) {
        				objYear = ele;
        			}
    			}
    			if(curYear<=objYear) {
    				/* ** 存在下一个年份值 直接设置该值 ** */
    				resDate.setYear(objYear);
    			}else {
    				/* ** 当前月份值已经超过范围中最大的年份值，设置个巨大值不再执行 ** */
    				resDate.setYear(9999);
    			}
            }else if(intervalYear>0) {
            	/* ** 间隔年触发 ** */
            	resDate.setYearApoint(intervalYear);
            }
        	if(upYear!=resDate.getYear()) {
        		if(iStep<1) {
        			resetMonth = true;
        		}
        	}
        }
        
        ////////////////////////////////重置之前的值，如果有必要的话///////////////////////////////////
        
        if(resetMonth) {
        	if(monthList.size()>1) {
        		resDate.setMonth(minMonth==null?1:minMonth);
        	}else if(monthList.size()==1) {
        		resDate.setMonth(monthList.get(0));
        	}else {
        		resDate.setMonth(1);
        	}
        	resetDay = true;
        }
    	if(resetDay) {
    		//月份进级，日期复位
    		if(dayList.size()>1) {
    			resDate.setDay(minDay==null?1:minDay);
    		}else if(dayList.size()==1) {
    			if(lastDayMode) {
    				resDate.setDay(resDate.getThisMonthDays()-dayList.get(0)+1);
    			}else {
    				resDate.setDay(dayList.get(0));
    			}
    		}else if(weekList.size()<1) {
    			resDate.setDay(1);
    		} else {
    			resDate = new SDate(resDate.getYearString()+"-"+resDate.getMonth()+"-01");
    			while(!weekList.contains(resDate.getDayOfWeek())) {
    				resDate.setDayApoint(1);
    			}
    		}
    		resetHour = true;
    	}
    	if(resetHour) {
    		//复位小时值
    		if(hourList.size()<1) {
    			resDate.setHour(0);
    		}else if(hourList.size()==1) {
    			resDate.setHour(hourList.get(0));
    		}else {
    			resDate.setHour(minHour==null?0:minHour);
    		}
    		resetMinute = true;
    	}
    	if(resetMinute) {
    		//复位分钟值
    		if(minuteList.size()<1) {
    			resDate.setMinute(0);
    		}else if(minuteList.size()==1) {
    			resDate.setMinute(minuteList.get(0));
    		}else {
    			resDate.setMinute(minMinute==null?0:minMinute);
    		}
    		resetSec = true;
    	}
    	if(resetSec) {
    		//复位秒值
    		if(secList.size()<1) {
    			resDate.setSecond(0);
    		}else if(secList.size()==1) {
    			resDate.setSecond(secList.get(0));
    		}else {
    			resDate.setSecond(minSec==null?0:minSec);
    		}
    		secChanged = true; //无用
    	}
        if(setDayAfterMonth) {
        	if(lastDayMode) {
        		resDate.setDay(resDate.getThisMonthDays()-dayList.get(0)+1);
        	}else if(lastWeekMode) {
        		resDate.setDay(SDate.getMonthLastWeekDay(weekList.get(0),resDate.getMonth(),resDate.getYear()));
        	}
        }
        if(workDayMode && (getWorkDayList().size()>0 || getUnWorkDayList().size()>0)) {
        	int count = 0; //防止死循环
        	String dateStr = resDate.getOnlyDate(); //待返回的日期
        	while(count<500 && (!getWorkDayList().contains(dateStr) || getUnWorkDayList().contains(dateStr))) {
        		resDate.setDayApoint(-1);
        		dateStr = resDate.getOnlyDate();
        		count++;
        	}
        }
    	return resDate;
    }
}
